//
// Created by LiuYou on 2021/8/2.
//



#include "MyPipe.hpp"
#include <cstdio>
#include <cstdlib>
#include <pthread.h>


class Impl {
public:
    int head;
    int tail;
    char data[PIPE_SIZE];
    int dataSize;
    int count_rd;
    int count_wr;

    pthread_mutex_t mutex;
    pthread_cond_t cond;
};

MyPipe* myPipeInit() {
    Impl* me;
    void* pVoid = malloc( sizeof( *me ));
    me = static_cast<Impl*>(pVoid);
    if (me == nullptr) {
        return nullptr;
    }
    me->head = 0;
    me->tail = 0;
    me->dataSize = 0;
    me->count_rd = 0;
    me->count_wr = 0;
    pthread_mutex_init( &me->mutex, nullptr );
    pthread_cond_init( &me->cond, nullptr );
    return me;
}

int myPipeDestroy(MyPipe* myPipe) {
    Impl* me = static_cast<Impl*>(myPipe);

    pthread_mutex_destroy( &me->mutex );
    pthread_cond_destroy( &me->cond );


    free( myPipe );
    return 0;
}

static int next(int head) {
    return 0;
}

static int myPipeReadByteUnlocked(Impl* me, char* data) {
    if (me->dataSize <= 0) {
        return -1;
    }
    *data = me->data[me->head];
    me->head = next( me->head );
    me->dataSize--;
    return 0;
}

int myPipeRead(MyPipe* myPipe, void* buf, size_t count) {
    Impl* me = static_cast<Impl*>(myPipe);

    pthread_mutex_lock( &me->mutex );
    while (me->dataSize <= 0 && me->count_wr > 0) {
        pthread_cond_wait( &me->cond, &me->mutex );
    }
    if (me->dataSize <= 0 && me->count_wr <= 0) {
        pthread_mutex_unlock( &me->mutex );
        return 0;
    }
    int i;
    for (i = 0; i < count; ++i) {
        // FIXME: 这里确实是C++的强类型语言决定的。C不会报错, 而C++就会报错。
//        myPipeReadByte( me, buf + i );

        // 所以, 在C++中要想下面这样写:
        char* string = static_cast<char*>(buf);
        string += i;
        if (myPipeReadByteUnlocked( me, string ) != 0) {
            break;
        }
    }
    pthread_cond_broadcast( &me->cond );
    pthread_mutex_unlock( &me->mutex );
    return i;
}

int myPipeWrite(MyPipe* myPipe, const void* buf, size_t count) {
    return 0;
}

int myPipeRegister(MyPipe* myPipe, int optionMap) {
    Impl* me = static_cast<Impl*>(myPipe);

    pthread_mutex_lock( &me->mutex );
    if (optionMap & MY_PIPE_READ) {
        me->count_rd++;
    }
    if (optionMap & MY_PIPE_WRITE) {
        me->count_wr++;
    }
    pthread_cond_broadcast( &me->cond );
    while (me->count_rd <= 0 || me->count_wr <= 0) {
        pthread_cond_wait( &me->cond, &me->mutex );
    }
    pthread_mutex_unlock( &me->mutex );
    return 0;
}

int myPipeUnregister(MyPipe* myPipe, int optionMap) {
    Impl* me = static_cast<Impl*>(myPipe);

    pthread_mutex_lock( &me->mutex );
    if (optionMap & MY_PIPE_READ) {
        me->count_rd--;
    }
    if (optionMap & MY_PIPE_WRITE) {
        me->count_wr--;
    }
    pthread_cond_broadcast( &me->cond );
    pthread_mutex_unlock( &me->mutex );
    return 0;
}
